package com.jackchen.common.transform;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.springframework.util.NumberUtils;

/**
 * po与vo相互转换器，均为静态方法，供action控制器调用
 * 
 * @author
 *
 */
public class Transformer {

	/**
	 * 将POJO集合转换为VO集合
	 * 
	 * @param pojoObjs	POJO对象集合
	 * @param voClass	VO模板
	 * @return
	 * @throws TransformException
	 */
	public static final <X> List<X> pojoToVos(Collection<? extends Object> pojoObjs, Class<X> voClass) throws TransformException {
		List<X> result = new ArrayList<X>();
		for (Object pojoObj : pojoObjs) {
			X element = pojoToVo(pojoObj, voClass);
			result.add(element);
		}
		return result;
	}
	
	/**
	 * 将POJO对象转换为VO
	 * 注意：POJO转换为VO的时候，规定： 1.回调方法最优先；
	 *                         2.如果不用回调方法实现值的转换，那么系统默认转换的时候要求VO类的属性字段类型均为JAVA基本类型
	 * 
	 * @param pojoObj	pojo实体对象
	 * @param voClass	vo对象模板
	 * @return
	 * @throws TransformException
	 */
	public static final <X> X pojoToVo(Object pojoObj, Class<X> voClass) throws TransformException  {
		if (pojoObj == null || voClass == null) {
			return null;
		}
		BindConvert ann = (BindConvert) voClass.getAnnotation(BindConvert.class);
		// 可能是基础数据
		if (ann == null) {
			if (pojoObj.getClass().equals(voClass)) {
				return (X) pojoObj;
			}
		}
		Class<?> pojoClass = ann.targetClass();
		
		// 传入的pojoObj与voClass里面配置的映射pojo不匹配
		if (!pojoClass.equals(pojoObj.getClass()) &&
				!pojoClass.equals(pojoObj.getClass().getSuperclass())) {
			throw new TransformException("You try to cover " + pojoObj.getClass().getName() + 
					" to " + voClass.getName() + 
					", But in your VO configuration, the VO object is mapping " + 
					pojoClass.getName());
		}
		
		// 根据VO类模板实例化一个VO对象
		Object result = null;
		try {
			 result = voClass.newInstance();
		} catch (Exception e) {
			throw new TransformException(e.getMessage(), e.fillInStackTrace());
		}

		// 解析POJO类模板，获得类名 
		String tarClassFullName = pojoObj.getClass().getName();
		if (pojoClass.equals(pojoObj.getClass().getSuperclass())) {
			tarClassFullName = pojoObj.getClass().getSuperclass().getName();
		}
		String[] tarClassNameSpl = tarClassFullName.split("\\.");
		String tarClassName = tarClassNameSpl[tarClassNameSpl.length - 1];
		
		// 获得vo模板里面所有Filed
		Field[] fields = voClass.getDeclaredFields();
		for (Field field : fields) {
			FieldConvert fieldAnn = field.getAnnotation(FieldConvert.class);
			if (fieldAnn == null) {
				continue;
			}
			String[] tarFieldNames = fieldAnn.targetFiledName();
			// 需要填充值的VO字段名
			String fieldName = field.getName();
			// 得到POJO为VO设置值的方法名，方法名规则是"属性名+From+POJO类名"
			String medName = fieldName + "From" + tarClassName;
			
			// 如果vo里面定义了转换的回调方法，则用回调方法优先来执行数据的转换
			Method method = null;
			try {
				// 检查VO里面是否定义了回调方法
				Method[] methods = voClass.getDeclaredMethods();
				for (Method voMethod : methods) {
					String name = voMethod.getName();
					if (medName.equals(name)) {
						method = voMethod;
						break;
					}
				}
				// POJO转换为VO的时候，如果vo类里面有"属性名+From+POJO类名"的方法时，将无视其它注解配置，优先调用该方法，在该方法里面将POJO类的值设置到VO中
				// VO转换为POJO的时候，如果vo类里面有"属性名+To+POJO类名"的方法时，将无视其它注解配置，优先调用该方法，在该方法里面将VO类的值设置到POJO中
				if (method != null) {
					method = voClass.getDeclaredMethod(medName, pojoClass);
					method.invoke(result, pojoObj);
					continue;
				}
			} catch (Exception e) {
				throw new TransformException(e.getMessage(), e.fillInStackTrace());
			}
			
			// 如果VO里面没有定义值之间转换的回调方法
			boolean isColl = fieldAnn.isCollection();
			Object tarVal = getModelValue(pojoObj, tarFieldNames);
			field.setAccessible(true);
			try {
				// 如果是将POJO里面的集合数据移植到VO的集合里
				if (isColl) {
					if(tarVal instanceof Collection){
						Collection<Object> tarValColl = (Collection<Object>) tarVal;
						Class elementType = fieldAnn.elementType();
						Collection<?> collVal = pojoToVos(tarValColl, elementType);
						field.set(result, collVal);
					}
				} else {
					Class fieldClass = field.getType();
					BindConvert bindConvert = (BindConvert) fieldClass.getAnnotation(BindConvert.class);
					if (bindConvert == null) {
						// 为vo里面的基础数据类型赋值
						Object voVal = null;
						if (tarVal != null) {
							voVal = convertValType(tarVal, field.getType(), fieldAnn.dateFormat());
						}
						field.set(result, voVal);
					} else {
						// 需要想vo里面的一个类型也为vo的属性赋值，需要回调本方法(参见RegionGroupVO类里面的regionsvo)
						// 此时tarVal是hibernate动态代码生成的pojo类的代理类(如Regions_assist_xxx)，代理类集成于原生类
						X entry = (X) pojoToVo(tarVal, fieldClass);
						field.set(result, entry);
					}
				}
			} catch (Exception e) {
				throw new TransformException(e.getMessage(), e.fillInStackTrace());
			}
		}
		
		return (X) result;
	}
	
	/**
	 * 将VO对象转换为POJO
	 * 
	 * @param voObj			VO类实例
	 * @param pojoClass		POJO类模板
	 * @return
	 * @throws TransformException
	 */
	public static final <Y> Y voToPojo(Object voObj, Class<Y> pojoClass) throws TransformException {
		assert(voObj != null && pojoClass != null);
		Class<? extends Object> voClass = voObj.getClass();
		BindConvert ann = (BindConvert) voClass.getAnnotation(BindConvert.class);
		// 可能是基础数据
//		if (ann == null) {
//			if (pojoObj.getClass().equals(voClass)) {
//				return (X) pojoObj;
//			}
//		}
		
		// 传入的pojoClass与voObj里面配置的映射pojo不匹配
		if (!pojoClass.equals(ann.targetClass())) {
			throw new TransformException("You try to cover " + voClass.getName() + 
					" to " + pojoClass.getName() + 
					", But in your VO configuration, the VO object is mapping " + 
					ann.targetClass().getName());
		}
		
		// 根据POJO类模板实例化一个POJO对象
		Object result = null;
		try {
			 result = pojoClass.newInstance();
		} catch (Exception e) {
			throw new TransformException(e.getMessage(), e.fillInStackTrace());
		}
		
		// 解析POJO类模板，获得类名 
		String tarClassFullName = pojoClass.getName();
		String[] tarClassNameSpl = tarClassFullName.split("\\.");
		String tarClassName = tarClassNameSpl[tarClassNameSpl.length - 1];
		
		// 获得vo模板里面所有Filed
		Field[] fields = voClass.getDeclaredFields();
		for (Field field : fields) {
			FieldConvert fieldAnn = field.getAnnotation(FieldConvert.class);
			String[] tarFieldNames = fieldAnn.targetFiledName();
			// VO字段名
			String fieldName = field.getName();
			// 得到VO为POJO设置值的方法名，方法名规则是"属性名+To+POJO类名"
			String medName = fieldName + "To" + tarClassName;
			// 如果vo里面定义了转换的回调方法，则用回调方法优先来执行数据的转换
			Method method = null;
			try {
				// 检查VO里面是否定义了回调方法
				Method[] methods = voClass.getDeclaredMethods();
				for (Method voMethod : methods) {
					String name = voMethod.getName();
					if (medName.equals(name)) {
						method = voMethod;
						break;
					}
				}
				if (method != null) {
					method = voClass.getDeclaredMethod(medName, pojoClass);
					method.invoke(voObj, result);
					continue;
				}
			} catch (Exception e) {
				throw new TransformException(e.getMessage(), e.fillInStackTrace());
			}
			
			// 如果VO里面没有定义值之间转换的回调方法
			Object voVal = null;
			try {
				field.setAccessible(true);
				// 获得VO对象里面的值
				voVal = field.get(voObj);
			} catch (Exception e) {
				throw new TransformException(e.getMessage(), e.fillInStackTrace());
			}
			
			boolean isCell = fieldAnn.isCollection();
			// 从VO的集合里面将值赋到POJO对象的集合中
			if (isCell) {
			} else {
				Object realVal = null;// VO里面的值做了类型转换之后，要放入POJO对象里面的真实值
				if (voVal != null) {
					Class<? extends Object> pojoFieldType = getModelFieldType(pojoClass, tarFieldNames);
					realVal = convertValType(voVal, pojoFieldType, fieldAnn.dateFormat());
				}
				setModelValue(result, tarFieldNames, realVal);
			}
		}
		return (Y) result;
	}
	
	/**
	 * 数据类型转换
	 * 
	 * @param tarVal		从对象里面取出来的值
	 * @param clazz			希望转成的数据类型
	 * @param dateFormat	如果VO里面属性字段是日期格式，则需要提供日期格式转换的格式
	 * @return
	 * @throws TransformException
	 */
	private static Object convertValType(Object tarVal, Class<? extends Object> clazz, String dateFormat) throws TransformException {
		if (String.class.equals(clazz)) {
			if (tarVal instanceof Date) {
				if (dateFormat == null || "".equals(dateFormat)) {
					dateFormat = "yyyy-MM-dd HH:mm:ss";// 默认日期格式
				}
				SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
				return sdf.format(tarVal);
			} else {
				return tarVal.toString();
			}
		} else if (Integer.class.equals(clazz)) {
			return NumberUtils.parseNumber(tarVal.toString(), Integer.class);
		} else if (Long.class.equals(clazz)) {
			return NumberUtils.parseNumber(tarVal.toString(), Long.class);
		} else if (Short.class.equals(clazz)) {
			return NumberUtils.parseNumber(tarVal.toString(), Short.class);
		} else if (Double.class.equals(clazz)) {
			return NumberUtils.parseNumber(tarVal.toString(), Double.class);
		} else if (Date.class.equals(clazz)) {
			if (dateFormat == null || "".equals(dateFormat)) {
				dateFormat = "yyyy-MM-dd HH:mm:ss";// 默认日期格式
			}
			SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
			try {
				return sdf.parse(tarVal.toString());
			} catch (ParseException e) {
				throw new TransformException(e.getMessage(), e.fillInStackTrace());
			}
		}
		return tarVal;
	}
	
	/**
	 * 返回POJO实体对象里面指定的属性值
	 * 
	 * @param pojoObj		POJO实体对象
	 * @param tarFieldNames	POJO对象的字段名
	 * @return
	 * @throws TransformException
	 */
	private static Object getModelValue(Object pojoObj, String[] tarFieldNames) throws TransformException {
		Object valueObj = pojoObj;
		for (String tarFieldName : tarFieldNames) {
			if (valueObj == null) {
				break;
			}
			try {
				String medName = "get" + String.valueOf(tarFieldName.charAt(0)).toUpperCase() + tarFieldName.substring(1, tarFieldName.length());
				Method getMethod = valueObj.getClass().getDeclaredMethod(medName);
				valueObj = getMethod.invoke(valueObj);
			} catch (Exception e) {
				throw new TransformException(e.getMessage(), e.fillInStackTrace());
			}
		}
		return valueObj;
	}
	
	/**
	 * 设置POJO对象里面的属性值
	 * 
	 * @param pojoObj			POJO对象
	 * @param tarFieldNames		POJO对象的字段名
	 * @param value				从VO里面取出来的要设置进去POJO的值
	 * @return
	 * @throws TransformException
	 */
	private static Object setModelValue(Object pojoObj, String[] tarFieldNames, Object value) throws TransformException {
		Object valueObj = pojoObj;
		for (String tarFieldName : tarFieldNames) {
			if (valueObj == null) {
				break;
			}
			try {
				Field field = valueObj.getClass().getDeclaredField(tarFieldName);
				field.setAccessible(true);
				field.set(pojoObj, value);
			} catch (Exception e) {
				throw new TransformException(e.getMessage(), e.fillInStackTrace());
			}
		}
		return valueObj;
	}
	
	/**
	 * 获取POJO将要设置值的属性的类型
	 * 
	 * @param pojoClass		pojo模板
	 * @param tarFieldNames	属性名路径
	 * @return
	 * @throws TransformException
	 */
	private static Class<? extends Object> getModelFieldType(Class<? extends Object> pojoClass, String[] tarFieldNames) throws TransformException {
		Class<? extends Object> clazz = pojoClass;
		for (String tarFieldName : tarFieldNames) {
			try {
				Field field = clazz.getDeclaredField(tarFieldName);
				clazz = field.getType();
			} catch (Exception e) {
				throw new TransformException(e.getMessage(), e.fillInStackTrace());
			}
		}
		return clazz;
	}
}
